#include "dfs.h"
#include "dfs_inner.h"



/* define  */

#define TAG "DFS-FS"

/* export func */

/**
 * @brief 文件系统注册
 * 
 * @param ops 
 * @return int 
 */
int dfs_fs_register(const struct dfs_filesystem_ops *ops)
{
    int ret = 0;
    int i;
    int sel = -1;
    const struct dfs_filesystem_ops *ops_iter;

    dfs_lock();

    for (i = 0; i < DFS_FILESYSTEM_TYPES_MAX; i++)
    {
        ops_iter = dfs_get_fs_ops_tbl(i);

        if (ops_iter == NULL) 
        {
            if (sel == -1) sel = i;
        }
        else 
        {
            if (strcmp(ops_iter->name, ops->name) == 0)
            {
                dfs_set_errno(-EEXIST);
                ret = -1;
                LOG_I(TAG, "fs (%s) is already register", ops->name);
                break;
            }
        }
    }

    if (sel == -1)
    {
        dfs_set_errno(-ENOSPC);
        LOG_E(TAG, "There is no space to register this file system (%s).", ops->name);
        ret = -1;
    }
    else if (ret == 0)
    {
        // LOG_I(TAG, "fs (%s) register in tbl[%d]", ops->name, sel);
        dfs_set_fs_ops_tbl(sel, ops);
    }

    dfs_unlock();

    return ret;
}

/**
 * @brief return the filesystem mounted on specified path
 * 
 * @param path 
 * @return struct dfs_filesystem* 
 */
struct dfs_filesystem *dfs_fs_lookup(const char *path)
{
    struct dfs_filesystem *fs_iter;
    struct dfs_filesystem *fs_ret = NULL;
    uint32_t fspath, prefixlen;
    int i;

    if (path == NULL)
    {
        LOG_E(TAG, "path null");
        return NULL;
    }

    prefixlen = 0;
    dfs_lock();
    for (i = 0; i < DFS_FILESYSTEMS_MAX; i++)
    {
        fs_iter = dfs_get_fs_tbl(i);
        
        if (fs_iter->path == NULL || fs_iter->ops == NULL)
        {
            // LOG_W(TAG, "fs_iter->path=0x%08X, fs_iter->ops=0x%08X", (uint32_t)fs_iter->path, (uint32_t)fs_iter->ops);
            continue;
        }

        fspath = strlen(fs_iter->path);
        if ((fspath < prefixlen) || (strncmp(fs_iter->path, path, fspath) != 0))
        {
            continue;
        }

        if (fspath > 1 && (strlen(path) > fspath) && (path[fspath] != '/'))
        {
            continue;
        }

        // LOG_I(TAG, "get fs_tbl[%d] = 0x%08X", i, fs_iter);
        fs_ret = fs_iter;
        prefixlen = fspath;
    }
    dfs_unlock();

    return fs_ret;
}


const char *dfs_fs_get_mounted_path(void *obj)
{
    /* not support yet */
    return NULL;
}

int dfs_fs_get_partition(void *obj)
{
    /* not support yet */
    return -1;
}

/**
 * @brief 文件系统挂载
 * 
 * @param dev_name 设备名称
 * @param path 挂载路径
 * @param fs_type 文件系统类型
 * @param rwflag 读写标志位
 * @param data 私有数据
 * @return int 
 */
int dfs_fs_mount(const char *dev_name, const char *path, const char *fs_type, uint32_t rwflag, const void *data)
{
    const struct dfs_filesystem_ops *ops;
    struct dfs_filesystem *iter;
    struct dfs_filesystem *fs = NULL;
    char *fullpath = NULL;
    int i;
    
    /* open specific device */
    if (dev_name == NULL)
    {
        LOG_E(TAG, "dev_name can't NULL");
    }

    /* find out the specific filesystem */
    dfs_lock();
    for (i = 0; i < DFS_FILESYSTEM_TYPES_MAX; i++)
    {
        ops = dfs_get_fs_ops_tbl(i);
        if ((ops != NULL) && (strcmp(ops->name, fs_type) == 0))
        {
            break;
        }
    }
    dfs_unlock();
    if (i == DFS_FILESYSTEM_TYPES_MAX)
    {
        LOG_E(TAG, "fs ops get fail (%d)", i);
        dfs_set_errno(-ENODEV);
        return -1;
    }

    /* check if there is mount implementation */
    if ((ops == NULL) || (ops->mount == NULL))
    {
        dfs_set_errno(-ENOSYS);
        return -1;
    }

    /* make full path for special file */
    fullpath = dfs_normalize_path(NULL, path);
    if (fullpath == NULL) /* not an abstract path */
    {
        dfs_set_errno(-ENOTDIR);
        return -1;
    }

    /* Check if the path exists or not, raw APIs call, fixme */
    if ((strcmp(fullpath, "/") != 0) && (strcmp(fullpath, "/dev") != 0))
    {
        struct dfs_fd fd;

        if (dfs_file_open(&fd, fullpath, O_RDONLY | O_DIRECTORY) < 0)
        {
            LOG_E(TAG, "dfs file open fail (%s)", fullpath);
            dfs_mem_free(fullpath);
            dfs_set_errno(-ENOTDIR);
            
            return -1;
        }
        dfs_file_close(&fd);
    }

    /* check whether the file system mounted or not  in the filesystem table
     * if it is unmounted yet, find out an empty entry */
    dfs_lock();

    for (i = 0; i < DFS_FILESYSTEMS_MAX; i++)
    {
        iter = dfs_get_fs_tbl(i);
        if (iter->ops == NULL)
        {
            (fs == NULL) ? (fs = iter) : 0;
        }
        else if (strcmp(iter->path, path) == 0)
        {
            dfs_set_errno(-EINVAL);
            goto err1;
        }
    }

    if ((fs == NULL) && (i == DFS_FILESYSTEMS_MAX))
    {
        dfs_set_errno(-ENOSPC);
        LOG_E(TAG, "There is no space to mount this file system (%s).", fs_type);
        goto err1;
    }

    /* register file system */
    fs->path = fullpath;
    fs->ops = ops;
    fs->dev_name = dev_name;
    // fs->dev_id = dev_id;
    /* release filesystem_table lock */
    dfs_unlock();

    /* call mount of this filesystem */
    if (ops->mount(fs, rwflag, data) < 0)
    {
        /* mount failed */
        dfs_lock();
        /* clear filesystem table entry */
        memset(fs, 0, sizeof(struct dfs_filesystem));

        goto err1;
    }

    return 0;

err1:
    dfs_unlock();
    dfs_mem_free(fullpath);

    return -1;
}

/**
 * @brief 文件系统卸载
 * 
 * @param path 
 * @return int 
 */
int dfs_fs_unmount(const char *path)
{
    char *fullpath;
    struct dfs_filesystem *iter;
    struct dfs_filesystem *fs = NULL;
    int i;

    fullpath = dfs_normalize_path(NULL, path);
    if (fullpath == NULL)
    {
        LOG_E(TAG, "fullpath create fail");
        dfs_set_errno(-ENOTDIR);
        return -1;
    }

    dfs_lock();

    for (i = 0; i < DFS_FILESYSTEMS_MAX; i++)
    {
        iter = dfs_get_fs_tbl(i);
        if ((iter->path != NULL) && (strcmp(iter->path, fullpath) == 0))
        {
            fs = iter;
            break;
        }
    }
    if (fs == NULL || fs->ops->unmount == NULL)
    {
        LOG_E(TAG, "fs unmount def fail");
        goto __exit;
    }
    if (fs->ops->unmount(fs) < 0)
    {
        LOG_E(TAG, "fs unmount fail");
        goto __exit;
    }

    /* close device, but do not check the status of device */
    // if (fs->dev_id != NULL)
    //     rt_device_close(fs->dev_id);

    if (fs->path != NULL)
    {
        dfs_mem_free(fs->path);
    }

    /* clear this filesystem table entry */
    memset(fs, 0, sizeof(struct dfs_filesystem));

    dfs_unlock();
    dfs_mem_free(fullpath);
    return 0;

__exit:
    dfs_unlock();
    dfs_mem_free(fullpath);
    return -1;
}

/**
 * @brief 文件系统创建(会进行磁盘格式化)
 * 
 * @param fs_name 
 * @param dev_name 
 * @return int 
 */
int dfs_fs_mkfs(const char *fs_name, const char *dev_name)
{
    int i;
    int ret = -1;
    const struct dfs_filesystem_ops *ops;

    // #warning "not finsh yet"

    dfs_lock();
    for (i = 0; i < DFS_FILESYSTEM_TYPES_MAX; i++)
    {
        ops = dfs_get_fs_ops_tbl(i);
        if (ops != NULL && strcmp(ops->name, fs_name) == 0)
        {
            break;
        }
    }
    dfs_unlock();

    if (ops == NULL)
    {
        LOG_E(TAG, "File system (%s) was not found.", fs_name);
        return -1;
    }

    if (ops->mkfs == NULL)
    {
        LOG_E(TAG,  "The file system (%s) mkfs function was not implement", fs_name);
        dfs_set_errno(-ENOSYS);
        return -1;
    }
    else
    {
        ret = ops->mkfs(NULL);
    }

    return ret;
}

/**
 * @brief 获取文件系统信息
 * 
 * @param path 
 * @param buffer 
 * @return int 
 */
int dfs_fs_statfs(const char *path, struct statfs *buffer)
{
    struct dfs_filesystem *fs;

    fs = dfs_fs_lookup(path);
    if (fs != NULL)
    {
        if (fs->ops->statfs != NULL)
        {
            return fs->ops->statfs(fs, buffer);
        }
    }
    return -1;
}

